﻿using System;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Media;

using iStudio.Structure.Engine;

namespace iStudio.Structure.Control.Audio.Editor
{
    public class EditorCore : System.Windows.Controls.Control
    {
        #region Fields

        //
        // AudioEngine
        //

        private AudioEngine _soundPlayer;

        //
        // Plugins
        //

        private EditorRenderer _renderer;
        private EditorTool _tool;

        //
        // Elements to render
        //

        public Border TimelineBorder;

        public Canvas WaveformCanvas;
        public Canvas TimelineCanvas;
        public Canvas RepeatCanvas;
        public Canvas ProgressCanvas;
        public Canvas TrackCanvas;

        public ScrollViewer WaveScroll;
        public ScrollViewer TrackScroll;
        public ScrollBar TimeBar;
        public ScrollBar WaveBar;

        public ScrollViewer TimeScroll;

        #endregion

        #region Dependency Properties

        #region PointThickness
        /// <summary>
        /// Identifies the <see cref="PointThickness" /> dependency property. 
        /// </summary>
        public static readonly DependencyProperty PointThicknessProperty = DependencyProperty.Register("PointThickness", typeof(double), typeof(EditorCore), new UIPropertyMetadata(0.3, OnPointThicknessChanged, OnCoercePointThickness));

        /// <summary>
        /// Gets or sets the width of point thickness used to draw waveforms
        /// </summary>        
        [Category("Common")]
        public double PointThickness
        {
            // IMPORTANT: To maintain parity between setting a property in XAML and procedural code, do not touch the getter and setter inside this dependency property!
            get
            {
                return (double)GetValue(PointThicknessProperty);
            }
            set
            {
                SetValue(PointThicknessProperty, value);
            }
        }

        private static object OnCoercePointThickness(DependencyObject o, object value)
        {
            EditorCore editorCore = o as EditorCore;

            if (editorCore != null)
            {
                return editorCore.OnCoercePointThickness((double)value);
            }

            return value;
        }

        private static void OnPointThicknessChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
        {
            EditorCore editorCore = o as EditorCore;

            if (editorCore != null)
            {
                editorCore.OnPointThicknessChanged((double)e.OldValue, (double)e.NewValue);
            }
        }

        /// <summary>
        /// Coerces the value of <see cref="PointThickness"/> when a new value is applied.
        /// </summary>
        /// <param name="value">The value that was set on <see cref="PointThickness"/></param>
        /// <returns>The adjusted value of <see cref="PointThickness"/></returns>
        protected virtual double OnCoercePointThickness(double value)
        {
            return value;
        }

        /// <summary>
        /// Called after the <see cref="PointThickness"/> value has changed.
        /// </summary>
        /// <param name="oldValue">The previous value of <see cref="PointThickness"/></param>
        /// <param name="newValue">The new value of <see cref="PointThickness"/></param>
        protected virtual void OnPointThicknessChanged(double oldValue, double newValue)
        {

        }

        #endregion

        #region RenderFrameSize
        /// <summary>
        /// Identifies the <see cref="RenderFrameSize" /> dependency property. 
        /// </summary>
        public static readonly DependencyProperty RenderFrameSizeProperty = DependencyProperty.Register("RenderFrameSize", typeof(int), typeof(EditorCore), new UIPropertyMetadata(128, OnRenderFrameSizeChanged, OnCoerceRenderFrameSize));

        /// <summary>
        /// Gets or sets the render frame size, which is the amount of detail rendering the data
        /// </summary>        
        [Category("Common")]
        public int RenderFrameSize
        {
            // IMPORTANT: To maintain parity between setting a property in XAML and procedural code, do not touch the getter and setter inside this dependency property!
            get
            {
                return (int)GetValue(RenderFrameSizeProperty);
            }
            set
            {
                SetValue(RenderFrameSizeProperty, value);
            }
        }

        private static object OnCoerceRenderFrameSize(DependencyObject o, object value)
        {
            EditorCore editorCore = o as EditorCore;

            if (editorCore != null)
            {
                return editorCore.OnCoerceRenderFrameSize((int)value);
            }

            return value;
        }

        private static void OnRenderFrameSizeChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
        {
            EditorCore editorCore = o as EditorCore;

            if (editorCore != null)
            {
                editorCore.OnRenderFrameSizeChanged((int)e.OldValue, (int)e.NewValue);
            }
        }

        /// <summary>
        /// Coerces the value of <see cref="RenderFrameSize"/> when a new value is applied.
        /// </summary>
        /// <param name="value">The value that was set on <see cref="RenderFrameSize"/></param>
        /// <returns>The adjusted value of <see cref="RenderFrameSize"/></returns>
        protected virtual int OnCoerceRenderFrameSize(int value)
        {
            return value;
        }

        /// <summary>
        /// Called after the <see cref="RenderFrameSize"/> value has changed.
        /// </summary>
        /// <param name="oldValue">The previous value of <see cref="RenderFrameSize"/></param>
        /// <param name="newValue">The new value of <see cref="RenderFrameSize"/></param>
        protected virtual void OnRenderFrameSizeChanged(int oldValue, int newValue)
        {

        }

        #endregion

        #region LeftLevelBrush

        /// <summary>
        /// Identifies the <see cref="LeftLevelBrush" /> dependency property. 
        /// </summary>
        public static readonly DependencyProperty LeftLevelBrushProperty = DependencyProperty.Register ("LeftLevelBrush", typeof (Brush), typeof (EditorCore), new UIPropertyMetadata (new SolidColorBrush (Colors.Blue), OnLeftLevelBrushChanged, OnCoerceLeftLevelBrush));

        /// <summary>
        /// Gets or sets a brush used to draw the left channel output on the waveform.
        /// </summary>        
        [Category ("Brushes")]
        public Brush LeftLevelBrush
        {
            // IMPORTANT: To maintain parity between setting a property in XAML and procedural code, do not touch the getter and setter inside this dependency property!
            get
            {
                return (Brush) GetValue (LeftLevelBrushProperty);
            }
            set
            {
                SetValue (LeftLevelBrushProperty, value);
            }
        }

        private static object OnCoerceLeftLevelBrush (DependencyObject o, object value)
        {
            EditorCore editorCore = o as EditorCore;

            if (editorCore != null)
            {
                return editorCore.OnCoerceLeftLevelBrush ((Brush) value);
            }

            return value;
        }

        private static void OnLeftLevelBrushChanged (DependencyObject o, DependencyPropertyChangedEventArgs e)
        {
            EditorCore editorCore = o as EditorCore;

            if (editorCore != null)
            {
                editorCore.OnLeftLevelBrushChanged ((Brush) e.OldValue, (Brush) e.NewValue);
            }
        }

        /// <summary>
        /// Coerces the value of <see cref="LeftLevelBrush"/> when a new value is applied.
        /// </summary>
        /// <param name="value">The value that was set on <see cref="LeftLevelBrush"/></param>
        /// <returns>The adjusted value of <see cref="LeftLevelBrush"/></returns>
        protected virtual Brush OnCoerceLeftLevelBrush (Brush value)
        {
            return value;
        }

        /// <summary>
        /// Called after the <see cref="LeftLevelBrush"/> value has changed.
        /// </summary>
        /// <param name="oldValue">The previous value of <see cref="LeftLevelBrush"/></param>
        /// <param name="newValue">The new value of <see cref="LeftLevelBrush"/></param>
        protected virtual void OnLeftLevelBrushChanged (Brush oldValue, Brush newValue)
        {

        }

        #endregion

        #region RightLevelBrush

        /// <summary>
        /// Identifies the <see cref="RightLevelBrush" /> dependency property. 
        /// </summary>
        public static readonly DependencyProperty RightLevelBrushProperty = DependencyProperty.Register ("RightLevelBrush", typeof (Brush), typeof (EditorCore), new UIPropertyMetadata (new SolidColorBrush (Colors.Red), OnRightLevelBrushChanged, OnCoerceRightLevelBrush));

        /// <summary>
        /// Gets or sets a brush used to draw the right speaker levels on the waveform.
        /// </summary>
        [Category ("Brushes")]
        public Brush RightLevelBrush
        {
            // IMPORTANT: To maintain parity between setting a property in XAML and procedural code, do not touch the getter and setter inside this dependency property!
            get
            {
                return (Brush) GetValue (RightLevelBrushProperty);
            }
            set
            {
                SetValue (RightLevelBrushProperty, value);
            }
        }

        private static object OnCoerceRightLevelBrush (DependencyObject o, object value)
        {
            EditorCore editorCore = o as EditorCore;

            if (editorCore != null)
            {
                return editorCore.OnCoerceRightLevelBrush ((Brush) value);
            }

            return value;
        }

        private static void OnRightLevelBrushChanged (DependencyObject o, DependencyPropertyChangedEventArgs e)
        {
            EditorCore editorCore = o as EditorCore;
            if (editorCore != null)
            {
                editorCore.OnRightLevelBrushChanged ((Brush) e.OldValue, (Brush) e.NewValue);
            }
        }

        /// <summary>
        /// Coerces the value of <see cref="RightLevelBrush"/> when a new value is applied.
        /// </summary>
        /// <param name="value">The value that was set on <see cref="RightLevelBrush"/></param>
        /// <returns>The adjusted value of <see cref="RightLevelBrush"/></returns>
        protected virtual Brush OnCoerceRightLevelBrush (Brush value)
        {
            return value;
        }

        /// <summary>
        /// Called after the <see cref="RightLevelBrush"/> value has changed.
        /// </summary>
        /// <param name="oldValue">The previous value of <see cref="RightLevelBrush"/></param>
        /// <param name="newValue">The new value of <see cref="RightLevelBrush"/></param>
        protected virtual void OnRightLevelBrushChanged (Brush oldValue, Brush newValue)
        {
   
        }

        #endregion

        #region ProgressBarBrush

        /// <summary>
        /// Identifies the <see cref="ProgressBarBrush" /> dependency property. 
        /// </summary>
        public static readonly DependencyProperty ProgressBarBrushProperty = DependencyProperty.Register ("ProgressBarBrush", typeof (Brush), typeof (EditorCore), new UIPropertyMetadata (new SolidColorBrush (Color.FromArgb (0xCD, 0xBA, 0x00, 0xFF)), OnProgressBarBrushChanged, OnCoerceProgressBarBrush));

        /// <summary>
        /// Gets or sets a brush used to draw the track progress indicator bar.
        /// </summary>
        [Category ("Brushes")]
        public Brush ProgressBarBrush
        {
            // IMPORTANT: To maintain parity between setting a property in XAML and procedural code, do not touch the getter and setter inside this dependency property!
            get
            {
                return (Brush) GetValue (ProgressBarBrushProperty);
            }
            set
            {
                SetValue (ProgressBarBrushProperty, value);
            }
        }

        private static object OnCoerceProgressBarBrush (DependencyObject o, object value)
        {
            EditorCore editorCore = o as EditorCore;

            if (editorCore != null)
            {
                return editorCore.OnCoerceProgressBarBrush ((Brush) value);
            }

            return value;
        }

        private static void OnProgressBarBrushChanged (DependencyObject o, DependencyPropertyChangedEventArgs e)
        {
            EditorCore editorCore = o as EditorCore;
            if (editorCore != null)
            {
                editorCore.OnProgressBarBrushChanged ((Brush) e.OldValue, (Brush) e.NewValue);
            }
        }

        /// <summary>
        /// Coerces the value of <see cref="ProgressBarBrush"/> when a new value is applied.
        /// </summary>
        /// <param name="value">The value that was set on <see cref="ProgressBarBrush"/></param>
        /// <returns>The adjusted value of <see cref="ProgressBarBrush"/></returns>
        protected virtual Brush OnCoerceProgressBarBrush (Brush value)
        {
            return value;
        }

        /// <summary>
        /// Called after the <see cref="ProgressBarBrush"/> value has changed.
        /// </summary>
        /// <param name="oldValue">The previous value of <see cref="ProgressBarBrush"/></param>
        /// <param name="newValue">The new value of <see cref="ProgressBarBrush"/></param>
        protected virtual void OnProgressBarBrushChanged (Brush oldValue, Brush newValue)
        {

        }

        #endregion

        #region ProgressBarThickness

        /// <summary>
        /// Identifies the <see cref="ProgressBarThickness" /> dependency property. 
        /// </summary>
        public static readonly DependencyProperty ProgressBarThicknessProperty = DependencyProperty.Register ("ProgressBarThickness", typeof (double), typeof (EditorCore), new UIPropertyMetadata (2.0d, OnProgressBarThicknessChanged, OnCoerceProgressBarThickness));

        /// <summary>
        /// Get or sets the thickness of the progress indicator bar.
        /// </summary>
        [Category ("Common")]
        public double ProgressBarThickness
        {
            // IMPORTANT: To maintain parity between setting a property in XAML and procedural code, do not touch the getter and setter inside this dependency property!
            get
            {
                return (double) GetValue (ProgressBarThicknessProperty);
            }
            set
            {
                SetValue (ProgressBarThicknessProperty, value);
            }
        }

        private static object OnCoerceProgressBarThickness (DependencyObject o, object value)
        {
            EditorCore editorCore = o as EditorCore;
            if (editorCore != null)
            {
                return editorCore.OnCoerceProgressBarThickness ((double) value);
            }
            return value;
        }

        private static void OnProgressBarThicknessChanged (DependencyObject o, DependencyPropertyChangedEventArgs e)
        {
            EditorCore editorCore = o as EditorCore;
            if (editorCore != null)
            {
                editorCore.OnProgressBarThicknessChanged ((double) e.OldValue, (double) e.NewValue);
            }
        }

        /// <summary>
        /// Coerces the value of <see cref="ProgressBarThickness"/> when a new value is applied.
        /// </summary>
        /// <param name="value">The value that was set on <see cref="ProgressBarThickness"/></param>
        /// <returns>The adjusted value of <see cref="ProgressBarThickness"/></returns>
        protected virtual double OnCoerceProgressBarThickness (double value)
        {
            value = Math.Max (value, 0.0d);
            return value;
        }

        /// <summary>
        /// Called after the <see cref="ProgressBarThickness"/> value has changed.
        /// </summary>
        /// <param name="oldValue">The previous value of <see cref="ProgressBarThickness"/></param>
        /// <param name="newValue">The new value of <see cref="ProgressBarThickness"/></param>
        protected virtual void OnProgressBarThicknessChanged (double oldValue, double newValue)
        {

        }

        #endregion

        #region RepeatRegionBrush

        /// <summary>
        /// Identifies the <see cref="RepeatRegionBrush" /> dependency property. 
        /// </summary>
        public static readonly DependencyProperty RepeatRegionBrushProperty = DependencyProperty.Register ("RepeatRegionBrush", typeof (Brush), typeof (EditorCore), new UIPropertyMetadata (new SolidColorBrush (Color.FromArgb (0x81, 0xF6, 0xFF, 0x00)), OnRepeatRegionBrushChanged, OnCoerceRepeatRegionBrush));

        /// <summary>
        /// Gets or sets a brush used to draw the repeat region on the waveform.
        /// </summary>
        [Category ("Brushes")]
        public Brush RepeatRegionBrush
        {
            // IMPORTANT: To maintain parity between setting a property in XAML and procedural code, do not touch the getter and setter inside this dependency property!
            get
            {
                return (Brush) GetValue (RepeatRegionBrushProperty);
            }
            set
            {
                SetValue (RepeatRegionBrushProperty, value);
            }
        }

        private static object OnCoerceRepeatRegionBrush (DependencyObject o, object value)
        {
            EditorCore editorCore = o as EditorCore;
            if (editorCore != null)
            {
                return editorCore.OnCoerceRepeatRegionBrush ((Brush) value);
            }
            return value;
        }

        private static void OnRepeatRegionBrushChanged (DependencyObject o, DependencyPropertyChangedEventArgs e)
        {
            EditorCore editorCore = o as EditorCore;
            if (editorCore != null)
            {
                editorCore.OnRepeatRegionBrushChanged ((Brush) e.OldValue, (Brush) e.NewValue);
            }
        }

        /// <summary>
        /// Coerces the value of <see cref="RepeatRegionBrush"/> when a new value is applied.
        /// </summary>
        /// <param name="value">The value that was set on <see cref="RepeatRegionBrush"/></param>
        /// <returns>The adjusted value of <see cref="RepeatRegionBrush"/></returns>
        protected virtual Brush OnCoerceRepeatRegionBrush (Brush value)
        {
            return value;
        }

        /// <summary>
        /// Called after the <see cref="RepeatRegionBrush"/> value has changed.
        /// </summary>
        /// <param name="oldValue">The previous value of <see cref="RepeatRegionBrush"/></param>
        /// <param name="newValue">The new value of <see cref="RepeatRegionBrush"/></param>
        protected virtual void OnRepeatRegionBrushChanged (Brush oldValue, Brush newValue)
        {

        }

        #endregion

        #region TimelineTickBrush

        /// <summary>
        /// Identifies the <see cref="TimelineTickBrush" /> dependency property. 
        /// </summary>
        public static readonly DependencyProperty TimelineTickBrushProperty = DependencyProperty.Register ("TimelineTickBrush", typeof (Brush), typeof (EditorCore), new UIPropertyMetadata (new SolidColorBrush (Colors.Black), OnTimelineTickBrushChanged, OnCoerceTimelineTickBrush));

        /// <summary>
        /// Gets or sets a brush used to draw the tickmarks on the timeline.
        /// </summary>
        [Category ("Brushes")]
        public Brush TimelineTickBrush
        {
            // IMPORTANT: To maintain parity between setting a property in XAML and procedural code, do not touch the getter and setter inside this dependency property!
            get
            {
                return (Brush) GetValue (TimelineTickBrushProperty);
            }
            set
            {
                SetValue (TimelineTickBrushProperty, value);
            }
        }

        private static object OnCoerceTimelineTickBrush (DependencyObject o, object value)
        {
            EditorCore editorCore = o as EditorCore;

            if (editorCore != null)
            {
                return editorCore.OnCoerceTimelineTickBrush ((Brush) value);
            }

            return value;
        }

        private static void OnTimelineTickBrushChanged (DependencyObject o, DependencyPropertyChangedEventArgs e)
        {
            EditorCore editorCore = o as EditorCore;

            if (editorCore != null)
            {
                editorCore.OnTimelineTickBrushChanged ((Brush) e.OldValue, (Brush) e.NewValue);
            }
        }

        /// <summary>
        /// Coerces the value of <see cref="TimelineTickBrush"/> when a new value is applied.
        /// </summary>
        /// <param name="value">The value that was set on <see cref="TimelineTickBrush"/></param>
        /// <returns>The adjusted value of <see cref="TimelineTickBrush"/></returns>
        protected virtual Brush OnCoerceTimelineTickBrush (Brush value)
        {
            return value;
        }

        /// <summary>
        /// Called after the <see cref="TimelineTickBrush"/> value has changed.
        /// </summary>
        /// <param name="oldValue">The previous value of <see cref="TimelineTickBrush"/></param>
        /// <param name="newValue">The new value of <see cref="TimelineTickBrush"/></param>
        protected virtual void OnTimelineTickBrushChanged (Brush oldValue, Brush newValue)
        {
  
        }

        #endregion

        #region AutoScaleWaveformCache

        /// <summary>
        /// Identifies the <see cref="AutoScaleWaveformCache" /> dependency property. 
        /// </summary>
        public static readonly DependencyProperty AutoScaleWaveformCacheProperty = DependencyProperty.Register ("AutoScaleWaveformCache", typeof (bool), typeof (EditorCore), new UIPropertyMetadata (false, OnAutoScaleWaveformCacheChanged, OnCoerceAutoScaleWaveformCache));

        /// <summary>
        /// Gets or sets a value indicating whether the waveform should attempt to autoscale
        /// its render buffer in size.
        /// </summary>
        /// <remarks>
        /// If true, the control will attempt to set the waveform's bitmap cache
        /// at a resolution based on the sum of all ScaleTransforms applied
        /// in the control's visual tree heirarchy. This can make the waveform appear
        /// less blurry if a ScaleTransform is applied at a higher level.
        /// The only ScaleTransforms that are considered here are those that have 
        /// uniform vertical and horizontal scaling (generally used to "zoom in"
        /// on a window or controls).
        /// </remarks>
        [Category ("Common")]
        public bool AutoScaleWaveformCache
        {
            // IMPORTANT: To maintain parity between setting a property in XAML and procedural code, do not touch the getter and setter inside this dependency property!
            get
            {
                return (bool) GetValue (AutoScaleWaveformCacheProperty);
            }
            set
            {
                SetValue (AutoScaleWaveformCacheProperty, value);
            }
        }

        private static object OnCoerceAutoScaleWaveformCache (DependencyObject o, object value)
        {
            EditorCore editorCore = o as EditorCore;

            if (editorCore != null)
            {
                return editorCore.OnCoerceAutoScaleWaveformCache ((bool) value);
            }

            return value;
        }

        private static void OnAutoScaleWaveformCacheChanged (DependencyObject o, DependencyPropertyChangedEventArgs e)
        {
            EditorCore editorCore = o as EditorCore;

            if (editorCore != null)
            {
                editorCore.OnAutoScaleWaveformCacheChanged ((bool) e.OldValue, (bool) e.NewValue);
            }
        }

        /// <summary>
        /// Coerces the value of <see cref="AutoScaleWaveformCache"/> when a new value is applied.
        /// </summary>
        /// <param name="value">The value that was set on <see cref="AutoScaleWaveformCache"/></param>
        /// <returns>The adjusted value of <see cref="AutoScaleWaveformCache"/></returns>
        protected virtual bool OnCoerceAutoScaleWaveformCache (bool value)
        {
            return value;
        }

        /// <summary>
        /// Called after the <see cref="AutoScaleWaveformCache"/> value has changed.
        /// </summary>
        /// <param name="oldValue">The previous value of <see cref="AutoScaleWaveformCache"/></param>
        /// <param name="newValue">The new value of <see cref="AutoScaleWaveformCache"/></param>
        protected virtual void OnAutoScaleWaveformCacheChanged (bool oldValue, bool newValue)
        {

        }

        #endregion

        #endregion

        #region Template Overrides

        /// <summary>
        /// When overridden in a derived class, is invoked whenever application code
        /// or internal processes call System.Windows.FrameworkElement.ApplyTemplate().
        /// </summary>
        public override void OnApplyTemplate ()
        {
            base.OnApplyTemplate ();

            WaveformCanvas = GetTemplateChild ("PART_Waveform") as Canvas;

            if (WaveformCanvas != null)
            {
                WaveformCanvas.CacheMode = new BitmapCache ();

                // Used to make the transparent regions clickable.
                WaveformCanvas.Background = new SolidColorBrush (Colors.Transparent);
            }

            //
            // Remove border
            //

            TimelineBorder = GetTemplateChild ("PART_TimelineBorder") as Border;

            if (TimelineBorder != null)
            {
                TimelineBorder.BorderThickness = new Thickness (0,0,0,0);
            }

            //waveformCanvas.Children.Add (centerLine);
            //waveformCanvas.Children.Add (leftPath);
            //waveformCanvas.Children.Add (rightPath);

            TimelineCanvas = GetTemplateChild ("PART_Timeline") as Canvas;
            RepeatCanvas = GetTemplateChild ("PART_Repeat") as Canvas;
            ProgressCanvas = GetTemplateChild ("PART_Progress") as Canvas;

            if (ProgressCanvas != null)
            {
                ProgressCanvas.Background = new SolidColorBrush(Colors.Transparent);
            }

            TimeScroll = GetTemplateChild ("PART_TimeScroll") as ScrollViewer;
            WaveScroll = GetTemplateChild ("PART_WaveScroll") as ScrollViewer;
            TrackScroll = GetTemplateChild ("PART_TrackScroll") as ScrollViewer;

            TimeBar = GetTemplateChild ("PART_TimeBar") as ScrollBar;
            WaveBar = GetTemplateChild ("PART_WaveBar") as ScrollBar;

            if (TimeBar != null)
            {
                TimeBar.Visibility = Visibility.Hidden;
            }

            if (WaveBar != null)
            {
                WaveBar.Visibility = Visibility.Hidden;
            }

            TrackCanvas = GetTemplateChild ("PART_Track") as Canvas;
        }

        /// <summary>
        /// Called whenever the control's template changes. 
        /// </summary>
        /// <param name="oldTemplate">The old template</param>
        /// <param name="newTemplate">The new template</param>
        protected override void OnTemplateChanged (ControlTemplate oldTemplate, ControlTemplate newTemplate)
        {
            base.OnTemplateChanged (oldTemplate, newTemplate);
            if (WaveformCanvas != null)
            {
                WaveformCanvas.Children.Clear ();
            }
            if (TimelineCanvas != null)
            {
                //_timelineCanvas.SizeChanged -= TimelineCanvasSizeChanged;
                TimelineCanvas.Children.Clear ();
            }
            if (RepeatCanvas != null)
            {
                RepeatCanvas.Children.Clear ();
            }
            if (ProgressCanvas != null)
            {
                ProgressCanvas.Children.Clear ();
            }
        }

        #endregion

        #region Constructor

        static EditorCore ()
        {
            DefaultStyleKeyProperty.OverrideMetadata (typeof (EditorCore), new FrameworkPropertyMetadata (typeof (EditorCore)));
        }

        #endregion

        #region Public Methods

        /// <summary>
        /// Register a sound player from which the waveform timeline
        /// can get the necessary playback data.
        /// </summary>
        /// <param name="soundPlayer">A sound player that provides waveform data through the IWaveformPlayer interface methods.</param>
        public void RegisterSoundPlayer (AudioEngine soundPlayer)
        {
            _soundPlayer = soundPlayer;

            // Register soundPlayers for renderer and tool

            if (_renderer != null)
            {
                _renderer.RegisterSoundPlayer (soundPlayer);
            }

            if (_tool != null)
            {
                _tool.RegisterSoundPlayer (soundPlayer);
            }
        }

        public void SetupRenderer(EditorRenderer renderer)
        {
            _renderer = renderer;
            _renderer.RegisterSoundPlayer(_soundPlayer);
            _renderer.RegisterTrackEditor(this);
        }

        public void SetupTool(EditorTool tool)
        {
            _tool = tool;
            _tool.RegisterSoundPlayer(_soundPlayer);
            _tool.RegisterTrackEditor(this);
        }

        protected override void OnRenderSizeChanged(SizeChangedInfo sizeInfo)
        {
            if (_renderer != null)
            {
                _renderer.OnRenderSizeChanged(sizeInfo);
            }

            if (_tool != null)
            {
                _tool.OnRenderSizeChanged(sizeInfo);
            }
        }

        /// <summary>
        /// Returns position in seconds from X coordinate in TrackEditor
        /// </summary>
        /// <param name="x"></param>
        /// <returns></returns>
        public double GetPositionInSeconds (double x)
        {
            return (Math.Floor(x / PointThickness) * RenderFrameSize) / _soundPlayer.Project.Format.SampleRate;
        }

        #endregion
    }
}